/*
 * regc_locale.c --
 *
 *	此文件包含特定于区域设置的正则表达式例程。
 *	此文件由regcomp.c包含。
 *
 * Copyright (c) 1998 by Scriptics Corporation.
 *
 * This software is copyrighted by the Regents of the University of
 * California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState
 * Corporation and other parties.  The following terms apply to all files
 * associated with the software unless explicitly disclaimed in
 * individual files.
 *
 * The authors hereby grant permission to use, copy, modify, distribute,
 * and license this software and its documentation for any purpose, provided
 * that existing copyright notices are retained in all copies and that this
 * notice is included verbatim in any distributions. No written agreement,
 * license, or royalty fee is required for any of the authorized uses.
 * Modifications to this software may be copyrighted by their authors
 * and need not follow the licensing terms described here, provided that
 * the new terms are clearly indicated on the first page of each file where
 * they apply.
 *
 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
 * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
 * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
 * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 * MODIFICATIONS.
 *
 * GOVERNMENT USE: If you are acquiring this software on behalf of the
 * U.S. government, the Government shall have only "Restricted Rights"
 * in the software and related documentation as defined in the Federal
 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
 * are acquiring the software on behalf of the Department of Defense, the
 * software shall be classified as "Commercial Computer Software" and the
 * Government shall have only "Restricted Rights" as defined in Clause
 * 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
 * authors grant the U.S. Government and others acting in its behalf
 * permission to use and distribute the software in accordance with the
 * terms specified in this license.
 *
 * src/backend/regex/regc_locale.c
 */

/* ASCII 字符名称表 */

static const struct cname
{
	const char *name;
	const char	code;
}			cnames[] =

{
	{
		"NUL", '\0'
	},
	{
		"SOH", '\001'
	},
	{
		"STX", '\002'
	},
	{
		"ETX", '\003'
	},
	{
		"EOT", '\004'
	},
	{
		"ENQ", '\005'
	},
	{
		"ACK", '\006'
	},
	{
		"BEL", '\007'
	},
	{
		"alert", '\007'
	},
	{
		"BS", '\010'
	},
	{
		"backspace", '\b'
	},
	{
		"HT", '\011'
	},
	{
		"tab", '\t'
	},
	{
		"LF", '\012'
	},
	{
		"newline", '\n'
	},
	{
		"VT", '\013'
	},
	{
		"vertical-tab", '\v'
	},
	{
		"FF", '\014'
	},
	{
		"form-feed", '\f'
	},
	{
		"CR", '\015'
	},
	{
		"carriage-return", '\r'
	},
	{
		"SO", '\016'
	},
	{
		"SI", '\017'
	},
	{
		"DLE", '\020'
	},
	{
		"DC1", '\021'
	},
	{
		"DC2", '\022'
	},
	{
		"DC3", '\023'
	},
	{
		"DC4", '\024'
	},
	{
		"NAK", '\025'
	},
	{
		"SYN", '\026'
	},
	{
		"ETB", '\027'
	},
	{
		"CAN", '\030'
	},
	{
		"EM", '\031'
	},
	{
		"SUB", '\032'
	},
	{
		"ESC", '\033'
	},
	{
		"IS4", '\034'
	},
	{
		"FS", '\034'
	},
	{
		"IS3", '\035'
	},
	{
		"GS", '\035'
	},
	{
		"IS2", '\036'
	},
	{
		"RS", '\036'
	},
	{
		"IS1", '\037'
	},
	{
		"US", '\037'
	},
	{
		"space", ' '
	},
	{
		"exclamation-mark", '!'
	},
	{
		"quotation-mark", '"'
	},
	{
		"number-sign", '#'
	},
	{
		"dollar-sign", '$'
	},
	{
		"percent-sign", '%'
	},
	{
		"ampersand", '&'
	},
	{
		"apostrophe", '\''
	},
	{
		"left-parenthesis", '('
	},
	{
		"right-parenthesis", ')'
	},
	{
		"asterisk", '*'
	},
	{
		"plus-sign", '+'
	},
	{
		"comma", ','
	},
	{
		"hyphen", '-'
	},
	{
		"hyphen-minus", '-'
	},
	{
		"period", '.'
	},
	{
		"full-stop", '.'
	},
	{
		"slash", '/'
	},
	{
		"solidus", '/'
	},
	{
		"zero", '0'
	},
	{
		"one", '1'
	},
	{
		"two", '2'
	},
	{
		"three", '3'
	},
	{
		"four", '4'
	},
	{
		"five", '5'
	},
	{
		"six", '6'
	},
	{
		"seven", '7'
	},
	{
		"eight", '8'
	},
	{
		"nine", '9'
	},
	{
		"colon", ':'
	},
	{
		"semicolon", ';'
	},
	{
		"less-than-sign", '<'
	},
	{
		"equals-sign", '='
	},
	{
		"greater-than-sign", '>'
	},
	{
		"question-mark", '?'
	},
	{
		"commercial-at", '@'
	},
	{
		"left-square-bracket", '['
	},
	{
		"backslash", '\\'
	},
	{
		"reverse-solidus", '\\'
	},
	{
		"right-square-bracket", ']'
	},
	{
		"circumflex", '^'
	},
	{
		"circumflex-accent", '^'
	},
	{
		"underscore", '_'
	},
	{
		"low-line", '_'
	},
	{
		"grave-accent", '`'
	},
	{
		"left-brace", '{'
	},
	{
		"left-curly-bracket", '{'
	},
	{
		"vertical-line", '|'
	},
	{
		"right-brace", '}'
	},
	{
		"right-curly-bracket", '}'
	},
	{
		"tilde", '~'
	},
	{
		"DEL", '\177'
	},
	{
		NULL, 0
	}
};

/* 
 * 以下数组定义有效的字符类名称。 
 * 条目必须与 regguts.h 中的 enum char_classes 匹配。 
 */
static const char *const classNames[NUM_CCLASSES + 1] = {
	"alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph",
	"lower", "print", "punct", "space", "upper", "xdigit", "word",
	NULL
};

/* 
 * 我们不使用 Tcl 的硬编码 Unicode 分类表。 
 * 这是因为 (a) 我们需要处理除 Unicode 之外的其他编码， 
 * (b) 我们希望尽可能密切跟踪 libc 区域例程的行为。 例如，一个 
 * 区域不将每个 Unicode 字母视为字母是可以理解的。因此，我们通过询问 
 * libc 来构建字符分类 cvec，即使对于 Unicode。 
 */


/* 
 * 元素 - 将排序元素名称映射到 chr 
 */
static chr element(struct vars *v,			/* 设置 */
		const chr *fc_startp,		/* 指向名称的开始 */
		const chr *fc_endp)		/* 指向名称末尾刚过的地方 */
{
	const struct cname *fc_cn;
	size_t		fc_len;

	/* 通用： one-chr 名称代表它们自己 */
	assert(fc_startp < fc_endp);
	fc_len = fc_endp - fc_startp;
	if (fc_len == 1)
		return *fc_startp;

	NOTE(REG_ULOCALE);

	/* 搜索表 */
	for (fc_cn = cnames; fc_cn->name != NULL; fc_cn++)
	{
		if (strlen(fc_cn->name) == fc_len &&
			pg_char_and_wchar_strncmp(fc_cn->name, fc_startp, fc_len) == 0)
		{
			break;				/* 注意突破 */
		}
	}
	if (fc_cn->name != NULL)
		return CHR(fc_cn->code);

	/* 找不到它 */
	ERR(REG_ECOLLATE);
	return 0;
}

/* 
 * 范围 - 提供范围的 cvec，包括合法性检查 
 */
static struct cvec * range(struct vars *v,			/* 设置 */
	  chr fc_a,					/* 范围起始 */
	  chr fc_b,					/* 范围结束，可能等于 a */
	  int fc_cases)				/* 大小写不敏感？ */
{
	int			fc_nchrs;
	struct cvec *fc_cv;
	chr			fc_c,
				fc_cc;

	if (fc_a != fc_b && !before(fc_a, fc_b))
	{
		ERR(REG_ERANGE);
		return NULL;
	}

	if (!fc_cases)
	{							/* 简易版本 */
		fc_cv = getcvec(v, 0, 1);
		NOERRN();
		addrange(fc_cv, fc_a, fc_b);
		return fc_cv;
	}

	/* 
	 * 当不区分大小写时，决定 cvec 范围是否可用是困难的， 
	 * 因此至少现在我们不会尝试。我们使用原始指定的 
	 * chrs 范围，然后将该范围外的任何大小写等价物 
	 * 作为单独的 chrs 添加上来。 
	 * 
	 * 为确保如果有人指定一个很大的范围时行为正常， 
	 * 将分配大小限制为 100000 个 chrs（任意）并在 
	 * 以下循环内部检查溢出。 
	 */
	fc_nchrs = fc_b - fc_a + 1;
	if (fc_nchrs <= 0 || fc_nchrs > 100000)
		fc_nchrs = 100000;

	fc_cv = getcvec(v, fc_nchrs, 1);
	NOERRN();
	addrange(fc_cv, fc_a, fc_b);

	for (fc_c = fc_a; fc_c <= fc_b; fc_c++)
	{
		fc_cc = pg_wc_tolower(fc_c);
		if (fc_cc != fc_c &&
			(before(fc_cc, fc_a) || before(fc_b, fc_cc)))
		{
			if (fc_cv->nchrs >= fc_cv->chrspace)
			{
				ERR(REG_ETOOBIG);
				return NULL;
			}
			addchr(fc_cv, fc_cc);
		}
		fc_cc = pg_wc_toupper(fc_c);
		if (fc_cc != fc_c &&
			(before(fc_cc, fc_a) || before(fc_b, fc_cc)))
		{
			if (fc_cv->nchrs >= fc_cv->chrspace)
			{
				ERR(REG_ETOOBIG);
				return NULL;
			}
			addchr(fc_cv, fc_cc);
		}
		if (CANCEL_REQUESTED(v->re))
		{
			ERR(REG_CANCEL);
			return NULL;
		}
	}

	return fc_cv;
}

/* 
 * before - chr x 是否在 chr y 之前，出于范围合法性目的？ 
 */
static int						/* 谓词 */
before(chr fc_x, chr fc_y)
{
	if (fc_x < fc_y)
		return 1;
	return 0;
}

/* 
 * eclass - 为等价类提供 cvec
 * 如果请求，必须包括大小写对应物。 
 */
static struct cvec * eclass(struct vars *v,			/* 设置 */
	   chr fc_c,					/* 表示等价类的排序元素。 */
	   int fc_cases)				/* 所有情况？ */
{
	struct cvec *fc_cv;

	/* 粗略的伪等价类用于测试 */
	if ((v->cflags & REG_FAKE) && fc_c == 'x')
	{
		fc_cv = getcvec(v, 4, 0);
		addchr(fc_cv, CHR('x'));
		addchr(fc_cv, CHR('y'));
		if (fc_cases)
		{
			addchr(fc_cv, CHR('X'));
			addchr(fc_cv, CHR('Y'));
		}
		return fc_cv;
	}

	/* 否则，无 */
	if (fc_cases)
		return allcases(v, fc_c);
	fc_cv = getcvec(v, 1, 0);
	assert(fc_cv != NULL);
	addchr(fc_cv, fc_c);
	return fc_cv;
}

/* 
 * lookupcclass - 根据名称查找字符类 
 * 
 * 失败时，在 *v 中设置错误代码；结果将是无效的。 
 */
static enum char_classes
lookupcclass(struct vars *v,	/* 上下文（用于返回错误） */
			 const chr *fc_startp, /* 名称开始的地方 */
			 const chr *fc_endp)	/* 名称末尾刚过的地方 */
{
	size_t		fc_len;
	const char *const *fc_namePtr;
	int			fc_i;

	/* 
	 * 将名称映射到相应的枚举值。 
	 */
	fc_len = fc_endp - fc_startp;
	for (fc_namePtr = classNames, fc_i = 0; *fc_namePtr != NULL; fc_namePtr++, fc_i++)
	{
		if (strlen(*fc_namePtr) == fc_len &&
			pg_char_and_wchar_strncmp(*fc_namePtr, fc_startp, fc_len) == 0)
			return (enum char_classes) fc_i;
	}

	ERR(REG_ECTYPE);
	return (enum char_classes) 0;
}

/* 
 * cclasscvec - 为字符类提供 cvec 
 * 
 * 如果 “cases” 为真，必须包括大小写对应物。 
 * 
 * 返回的 cvec 可能是从 getcvec() 获取的暂时 cvec， 
 * 或从 pg_ctype_get_cache() 缓存的永久 cvec。这是可以的 
 * 因为调用者无论如何都不需要显式释放结果。 
 */
static struct cvec * cclasscvec(struct vars *v,		/* 设置 */
		   enum char_classes fc_cclasscode,	/* 要构建 cvec 的类 */
		   int fc_cases)			/* 大小写不敏感？ */
{
	struct cvec *fc_cv = NULL;

	/*
	 * 如果匹配不区分大小写，则将小写和大写重映射为字母。
	 */

	if (fc_cases &&
		(fc_cclasscode == CC_LOWER ||
		 fc_cclasscode == CC_UPPER))
		fc_cclasscode = CC_ALPHA;

	/*
	 * 现在计算字符类的内容。对于基于<wctype.h>或<ctype.h>函数行为的类，我们使用
	 * pg_ctype_get_cache，以便缓存结果。其他类在这里有硬编码的定义，对于这些类，我们只是
	 * 动态构造一个临时的cvec。
	 *
	 * 注意：保持这段代码与下面的cclass_column_index()同步。
	 */

	switch (fc_cclasscode)
	{
		case CC_PRINT:
			fc_cv = pg_ctype_get_cache(pg_wc_isprint, fc_cclasscode);
			break;
		case CC_ALNUM:
			fc_cv = pg_ctype_get_cache(pg_wc_isalnum, fc_cclasscode);
			break;
		case CC_ALPHA:
			fc_cv = pg_ctype_get_cache(pg_wc_isalpha, fc_cclasscode);
			break;
		case CC_WORD:
			fc_cv = pg_ctype_get_cache(pg_wc_isword, fc_cclasscode);
			break;
		case CC_ASCII:
			/* 硬编码的含义 */
			fc_cv = getcvec(v, 0, 1);
			if (fc_cv)
				addrange(fc_cv, 0, 0x7f);
			break;
		case CC_BLANK:
			/* 硬编码的含义 */
			fc_cv = getcvec(v, 2, 0);
			addchr(fc_cv, '\t');
			addchr(fc_cv, ' ');
			break;
		case CC_CNTRL:
			/* 硬编码的含义 */
			fc_cv = getcvec(v, 0, 2);
			addrange(fc_cv, 0x0, 0x1f);
			addrange(fc_cv, 0x7f, 0x9f);
			break;
		case CC_DIGIT:
			fc_cv = pg_ctype_get_cache(pg_wc_isdigit, fc_cclasscode);
			break;
		case CC_PUNCT:
			fc_cv = pg_ctype_get_cache(pg_wc_ispunct, fc_cclasscode);
			break;
		case CC_XDIGIT:

			/*
			 * 在非西方区域，如何定义这一点并不清楚，
			 * 更不清楚尝试这样做是否有任何特殊用途。所以
			 * 只需硬编码其含义。
			 */
			fc_cv = getcvec(v, 0, 3);
			if (fc_cv)
			{
				addrange(fc_cv, '0', '9');
				addrange(fc_cv, 'a', 'f');
				addrange(fc_cv, 'A', 'F');
			}
			break;
		case CC_SPACE:
			fc_cv = pg_ctype_get_cache(pg_wc_isspace, fc_cclasscode);
			break;
		case CC_LOWER:
			fc_cv = pg_ctype_get_cache(pg_wc_islower, fc_cclasscode);
			break;
		case CC_UPPER:
			fc_cv = pg_ctype_get_cache(pg_wc_isupper, fc_cclasscode);
			break;
		case CC_GRAPH:
			fc_cv = pg_ctype_get_cache(pg_wc_isgraph, fc_cclasscode);
			break;
	}

	/* 如果cv现在为NULL，原因必须是“内存不足” */
	if (fc_cv == NULL)
		ERR(REG_ESPACE);
	return fc_cv;
}

/*
 * cclass_column_index - 获取chr的适当高色彩图列索引
 */
static int cclass_column_index(struct colormap *cm, chr fc_c)
{
	int			colnum = 0;

	/* 对于简单的chr，没必要进行这些繁琐的操作 */
	assert(fc_c > MAX_SIMPLE_CHR);

	/*
	 * 注意：我们不应该看到请求考虑不被cclasscvec()视为区域特定的cclasses。
	 */
	if (cm->classbits[CC_PRINT] && pg_wc_isprint(fc_c))
		colnum |= cm->classbits[CC_PRINT];
	if (cm->classbits[CC_ALNUM] && pg_wc_isalnum(fc_c))
		colnum |= cm->classbits[CC_ALNUM];
	if (cm->classbits[CC_ALPHA] && pg_wc_isalpha(fc_c))
		colnum |= cm->classbits[CC_ALPHA];
	if (cm->classbits[CC_WORD] && pg_wc_isword(fc_c))
		colnum |= cm->classbits[CC_WORD];
	assert(cm->classbits[CC_ASCII] == 0);
	assert(cm->classbits[CC_BLANK] == 0);
	assert(cm->classbits[CC_CNTRL] == 0);
	if (cm->classbits[CC_DIGIT] && pg_wc_isdigit(fc_c))
		colnum |= cm->classbits[CC_DIGIT];
	if (cm->classbits[CC_PUNCT] && pg_wc_ispunct(fc_c))
		colnum |= cm->classbits[CC_PUNCT];
	assert(cm->classbits[CC_XDIGIT] == 0);
	if (cm->classbits[CC_SPACE] && pg_wc_isspace(fc_c))
		colnum |= cm->classbits[CC_SPACE];
	if (cm->classbits[CC_LOWER] && pg_wc_islower(fc_c))
		colnum |= cm->classbits[CC_LOWER];
	if (cm->classbits[CC_UPPER] && pg_wc_isupper(fc_c))
		colnum |= cm->classbits[CC_UPPER];
	if (cm->classbits[CC_GRAPH] && pg_wc_isgraph(fc_c))
		colnum |= cm->classbits[CC_GRAPH];

	return colnum;
}

/*
 * allcases - 为chr的所有大小写对应提供cvec（包括它本身）
 *
 * 这是对简单字符的快捷方式，最好是高效的；
 * 麻烦的情况通过range()进行处理。
 */
static struct cvec * allcases(struct vars *v,		/* 设置 */
		 chr fc_c)					/* 要获取大小写对应的字符 */
{
	struct cvec *fc_cv;
	chr			fc_lc,
				fc_uc;

	fc_lc = pg_wc_tolower(fc_c);
	fc_uc = pg_wc_toupper(fc_c);

	fc_cv = getcvec(v, 2, 0);
	addchr(fc_cv, fc_lc);
	if (fc_lc != fc_uc)
		addchr(fc_cv, fc_uc);
	return fc_cv;
}

/*
 * cmp - chr-子串比较
 *
 * 回溯需要这个。它最好是高效的。
 * 注意，它不需要报告任何信息，除了相等/不等。
 * 还要注意长度是精确的，比较不应该在嵌入的NUL处停止！
 */
static int						/* 相等时为0，不相等时为非零 */
cmp(const chr *fc_x, const chr *fc_y, /* 要比较的字符串 */
	size_t fc_len)					/* 比较的精确长度 */
{
	return memcmp(VS(fc_x), VS(fc_y), fc_len * sizeof(chr));
}

/*
 * casecmp - 不区分大小写的chr-子串比较
 *
 * REG_ICASE回溯需要这个。它最好是高效的。
 * 注意，它不需要报告任何信息，除了相等/不等。
 * 还要注意长度是精确的，比较不应该在嵌入的NUL处停止！
 */
static int						/* 相等时为0，不相等时为非零 */
casecmp(const chr *fc_x, const chr *fc_y, /* 要比较的字符串 */
		size_t fc_len)				/* 比较的精确长度 */
{
	for (; fc_len > 0; fc_len--, fc_x++, fc_y++)
	{
		if ((*fc_x != *fc_y) && (pg_wc_tolower(*fc_x) != pg_wc_tolower(*fc_y)))
			return 1;
	}
	return 0;
}
